//测试： char arr[] = "abcd" 是常量字符串吗
#ifdef a
#include<iostream>
using namespace std;
int main() {
	char arr[] = "abcd";
	cout << arr << endl;
	arr[0] = 'f';
	cout << arr << endl;
	///////////////////
	// char* temp = "abcde"; -->c++里面这种写法是错误的，应该加上const,在代码段
	const char* temp = "abcde";
	cout << temp << endl;
	return 0;
}
#endif 


#ifdef a
//c语言中的测试
//测试： char arr[] = "abcd" 是常量字符串吗
#include<stdio.h>
int main() {
	char* char1 = "abcde";
	printf("%s\n", char1);
	char1[1] = 'd';//代码段不允许被更改，是只读类型，调试时此处异常
	printf("%s\n", char1);
	return 0;
}
#endif 

//结论：字符数组本身存储在栈上或数据段中
//注意：用来给字符数组进行初始化的字符串是存储在代码段中的，属于不可更改内容，也就是说，首先在代码段有了一个字符串
// 然后用该字符串对开辟在栈上的字符数组进行初始化，之后数组就是数组，其内容本身存储在栈上，可更改

//内核空间/栈/内存映射段（文件映射、动态库、匿名映射）（一个共享的动态内存库）/数据段/代码段


#ifdef a
//c++的内存管理
//new和delete不是函数，而是c++中提供的关键字
//new默认情况下是从堆上申请空间的
//new+类型 
#include<iostream>
using namespace std;
int main() {
	//单个类型空间的申请
	int* ptr = new int;//申请一个int类型大小的空间(不需要判空，只要执行成功就不为空/不需要类型转换，本身就返回申请类型指针)
	double* ptr1 = new double(3.14);//申请时的初始化方法  
	//单个释放
	delete ptr1;
	delete ptr;

	//一段连续空间的申请
	int array[10];//其类型为：int[10]
	int* arr = new int[10]{ 1,2,3,4,5,6,7,8};//使用花括号初始化,是c++11的新语法
	//连续空间释放
	delete[]arr;
 	return 0;
}
//优势
//不需要头文件（关键字）
//不需要类型转换（直接返回对应指针类型）
//不需要判空，执行完就不会为空
//初始化更加灵活

//注意
//new--->delete
//new[]--->delete[]
///malloc/calloc/realloc--->free
//不匹配可能会导致内存泄漏或者程序崩溃
#endif 

#ifdef a
//_CrtDumpMemoryLeaks输出返回值检测是不是有问题
//申请释放的方式不匹配是否会出现问题--》测试
#include<iostream>
using namespace std;
int main() {
	int* p1 = new int;
	delete[]p1;
	cout << _CrtDumpMemoryLeaks() << endl;

	int* p2 = new int;
	free(p2);
	cout << _CrtDumpMemoryLeaks() << endl;


	int* p3 = (int*)malloc(sizeof(int));
	delete p3;
	cout << _CrtDumpMemoryLeaks() << endl;

	int* p4 = (int*)malloc(sizeof(int));
	delete[]p4;
	cout << _CrtDumpMemoryLeaks() << endl;

	int* p5 = new int[5];
	free(p5);
	cout << _CrtDumpMemoryLeaks() << endl;

	int* p6 = new int[5];
	delete p6;
	cout << _CrtDumpMemoryLeaks() << endl;

	int* p7 = (int*)malloc(sizeof(int) * 5);
	delete p7;
	cout << _CrtDumpMemoryLeaks() << endl;

	int* p8 = (int*)malloc(sizeof(int) * 5);
	free(p8);
	cout << _CrtDumpMemoryLeaks() << endl;
	//对于内置类型空间申请和释放，是否匹配起来使用，不影响，但有利于养成良好习惯
	return 0;
}
#endif 

#ifdef a
#include<iostream>
using namespace std;
int main() {
	int* pa = new int;
	cout << _CrtDumpMemoryLeaks() << endl;
	delete[]pa;
	cout << _CrtDumpMemoryLeaks() << endl;
	cout << _CrtDumpMemoryLeaks() << endl;
	cout << _CrtDumpMemoryLeaks() << endl;
	cout << _CrtDumpMemoryLeaks() << endl;
	return 0;
}
#endif 


#ifdef a
#include<iostream>
using namespace std;
class Test {
public:
	Test() {
		_t = 10;
		_p = (int*)malloc(sizeof(int) * 10);
		cout << "Test" << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test" << endl;
	}
private:
	int* _p;
	int _t;
};
void MyTest() {
	
	/*
	测试点一
	Test* p1 = (Test*)malloc(sizeof(Test));   
	 // malloc不会调用构造函数初始化对象,只是从堆上划分了一定的空间
	 //因为malloc连构造函数都没有进行调用，所以只能称之为相同大小的内存块，
	 //而不能称之为对象

	 Test* p2 = (Test*)malloc(sizeof(Test)* 10);
	 delete p1;   // 崩溃了---delete在销毁时需要调用相应类型的析构函数
	 delete[] p2;	//未调用构造函数对_p指针进行初始化化，直接free野指针，崩溃
	 */
	

	//测试点二
	/*
	Test* p3 = new Test;  // new---》调用构造函数
	free(p3);  //free不会调用析构函数，导致内存泄漏
	//free只是把p3指向的空间释放掉，而没有释放掉自定义类型中构造函数申请的空间
	//_CrtDumpMemoryLeaks();-->内存泄漏
	*/

	//测试点三
	/*
	Test* p4 = new Test;
	delete[] p4;
	*/

	//测试点四
	Test* p6 = new Test;
	delete p6;
	cout << _CrtDumpMemoryLeaks() << endl;
	//测试点五
	/*
	Test* p5 = new Test[10];
	delete[] p5;
	*/
}
int main() {
	MyTest();
}


//疑问：
// 1.连续删除和单个删除的区别
// 2.测试点4为什么不发生内存泄漏
//		在计算机科学中，内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。
//		内存泄漏并非指内存在物理上的消失，而是应用程序分配某段内存后
//		，由于设计错误，导致在释放该段内存之前就失去了对该段内存的控制，从而造成了内存的浪费。

//在内置类型中：
//new []不会在首地址前4个字节定义数组长度。
//delete 和 delete[]是一样的执行效果，都会删除整个数组，要删除的长度从new时即可知道。

//自定义类型中
//new []会在首地址前4个字节定义数组长度。
//当delete[]时，会根据前4个字节所定义的长度来执行析构函数删除整个数组。
//如果只是delete数组首地址，只会删除第一个对象的值。

//malloc：只负责从堆上申请size个字节空间，并不会对空间中的内容进行初始化，即不会调用构造函数
//free：只负责将p指向的堆空间进行释放，并不会调用析构函数对空间中的内容进行清理，即不会调用析构哈数
//new：会申请空间，其次会调用构造函数对空间中的内容进行初始化
//delete：会释放对象空间，并且会对空间中的资源进行清理，即调用析构函数


//原理；
//new Test(自定义类型)
//申请sizeof(T)大小的空间
// 
//调用T类中的构造函数对堆空间进行初始化形成对象

#endif 


#ifdef a
#include<iostream>
using namespace std;

class Test {
public:
	Test() {
		_t = 10;
		_p = (int*)malloc(sizeof(int) * 10);
		cout << "Test" << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test" << endl;
	}
private:
	int* _p;
	int _t;
};

void MyTest() {
	//当构造函数由参数时--》解决方法
	//写默认构造函数
	//写全缺省构造函数
	//new时用（）传入参数，同函数类似 
}
int main() {
	MyTest();
	return 0;
}
#endif 


#ifdef a
//空与0
#include<iostream>
using namespace std;
int main() {
	int* ptr = nullptr;
	int* ptr1 = NULL;
	if (ptr == 0)
		cout << "ptr" << endl;
	if (ptr1 == 0)
		cout << "ptr1" << endl;
	return 0;
}
//均输出
#endif 

#ifdef a
//new和delete的内在逻辑
#include<iostream>
using namespace std;

class Test {
public:
	Test() {
		_t = 10;
		_p = (int*)malloc(sizeof(int) * 10);
		cout << "Test" << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test" << endl;
	}
private:
	int* _p;
	int _t;
};

void MyTest() {
	Test* pt = new Test;
}
int main() {
	MyTest();
	return 0;
}

//new
//首先调用函数void* operator new(size)来申请空间
//内部使用malloc申请空间，若申请成功则返回空间首地址
//空间不足的应对措施：用户提供应对方法
//if(_callnewh(size) == 0 )通过函数指针调用函数用来检验是否用户提供了相应的应对措施
//若有提供，则执行提供函数（目的，让系统拥有更多的可用堆内存），无提供，则进入if语句，抛出异常，让程序处于非法情况。
	
//然后调用类的构造函数初始化空间形成实例化对象


	//delete
//先调用析构函数，将堆中对象申请的资源清理掉
//释放对象的空间--void operator delete(void *p),实际上也是通过free进行释放的
//结构体：_CrtMemBlockHeader * pHead; 管理内存空间，通常位于申请空间上方，32位占32个byte空间
//申请空间的结尾需要越界保护机制，32位下4个字节


#endif 


#ifdef a
//new连续空间
//Test* _ptr = new Test[N];--》申请N个类空间（类数组）
//首先调用void* operator new[](size_t size) -->size: sizeof(Test) * N
// ---》实际其内部为void* operator new(size_t size)----》malloc
//调用N次构造函数将申请的N个T类型大小的对象空间初始化为N个对象
//当申请一段自定义类型对象的连续空间时，类必须要提供默认/全缺省的构造函数，否则无法传参调用构造函数
//(因为调用N次构造函数需要传参多次，较为繁琐，不支持)

//delete[N] _ptr
#include<iostream>
using namespace std;
class Test {
public:
	Test(int t = 10) 
	:_t(t)
	{
		_p = (int*)malloc(sizeof(int) * 10);
		cout << "Test: " << this << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test: " << this << endl;
	}
private:
	int* _p;
	int _t;
};
void MyTest() {
	Test* pt = new Test[10];//需要有默认/全缺省构造函数
	//申请的地址空间从小到大连续存放，差值为sizeof(Test)
	cout << endl;
	delete[10] pt;
	//先调用N次析构函数-->从后往前析构，释放每个对象中的资源
	//调用 void operator delete[](void* ptr)--->void operator delete(void* ptr)---->free
	//---》依次释放对象空间，释放空间顺序与申请相反，即地址由高到低，差值为sizeof(Test);

}
int main() {
	MyTest();
	return 0;
}
#endif

//操作符new/delete是关键字，通常用来指对应的operator new/operator delete函数
//操作符new/delete是可以进行重载的，但一般情况下不会重载，除非有一些特殊需求
//如发生内存泄漏时，可以进行重载，打印申请空间，检查内存泄漏 


#ifdef a
#include<iostream>
using namespace std;
class Test {
public:
	Test(int t = 10)
		:_t(t)
	{
		_p = (int*)malloc(sizeof(int) * 5);
		cout << "Test: " << this << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test: " << this << endl;
	}

	//重载为类的成员函数（也可为全局函数，无影响） 
	/*
	无意义的重载
	void* operator new(size_t size) {
		return malloc(size);
	}
	void operator delete(void* p) {
		if (p) {
			free(p);
			p = nullptr;
		}
	}
	*/

	//new的重载函数第一个参数必须为size_t 类型
	/*
	void* operator new(int size) {
		return malloc(size);
	}
	*/
	//否则报错： 分配函数的第一个参数必须是"size_t" 类型

private:
	int _t;
	int* _p;
};

//重载为全局函数
//无意义的重载
/*
void* operator new(size_t size) {
	cout << "new" << endl;
	return malloc(size);
}
void operator delete(void* p) {
	if (p) {
		free(p);
		p = nullptr;
	}
}
*/


void* operator new(size_t size, const char* file_name, const char* function_name, int line_num) {
	//文件名--函数名--行数
	cout << file_name << '-' << function_name << '-' << line_num << ':' << size  << endl;
	//return malloc(size);
	return operator new(size);
}

//delete函数的重载第一个参数类型必须为void*
void operator delete(void* p, const char* file_name, const char* function_name, int line_num) {
	cout << file_name << '-' << function_name << '-' << line_num << endl;
	//free(p);
	//p = nullptr;
	return operator delete(p);
}

#define new new(__FILE__, __FUNCDNAME__, __LINE__)
void MyTest() {
	//重载后的new函数多了参数，需要在new后面传参
	//Test* ptr = new(__FILE__, __FUNCDNAME__, __LINE__) Test;-->麻烦，使用宏替换
	Test* ptr = new Test;
	//delete(__FILE__, __FUNCDNAME__, __LINE__) ptr;--->错误的，无法进行直接传参调用
	ptr->~Test();//delete重载后（改变了参数）需要显性定义析构函数的调用
	operator delete(ptr, __FILE__, __FUNCDNAME__, __LINE__);//函数调用
	//可以运行，但问题是，这样只调用了delete函数，但没有调用析构函数，便需要显性调用
}
int main() {
	MyTest();
	return 0; 
}
#endif 


#ifdef a
//delete重载后调用麻烦，尝试进行封装
#include<iostream>
using namespace std;
class Test {
public:
	Test(int t = 10)
		:_t(t)
	{
		_p = (int*)malloc(sizeof(int) * 5);
		cout << "Test: " << this << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test: " << this << endl;
	}

private:
	int _t;
	int* _p;
};

void* operator new(size_t size, const char* file_name, const char* function_name, int line_num) {
	cout << file_name << '-' << function_name << '-' << line_num << ':' << size << endl;
	return operator new(size);
}

//-----------------------------------------------------------------------
//delete 封装
void operator delete(void* p, const char* file_name, const char* function_name, int line_num) {
	cout << file_name << '-' << function_name << '-' << line_num << endl;
	//return operator delete(p);--->这样写的话进入无限递归
	//简写一下，不建议
	free(p);
	p = nullptr;

}

void operator delete(void* p) {
	operator delete(p, __FILE__, __FUNCDNAME__, __LINE__);
}
//----------------------------------------------------------------------------------
#define new new(__FILE__, __FUNCDNAME__, __LINE__)


void MyTest() {
	Test* ptr = new Test;
	delete ptr;
}
int main() {
	MyTest();
	return 0;
}
#endif 

#ifdef a
//delete对析构的调用
#include<iostream>
using namespace std;
class Test {
public:
	Test(int t = 10)
		:_t(t)
	{
		_p = (int*)malloc(sizeof(int) * 5);
		cout << "Test: " << this << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test: " << this << endl;
	}

private:
	int _t;
	int* _p;
};

void MyTest() {
	Test* ptr = new Test;
	delete ptr;//反汇编je指令-》跳转，此处为跳转到析构函数
}
int main() {
	MyTest();
	return 0;
}
#endif 

#ifdef a
//将delete写在函数中，是否会调用析构 测试
#include<iostream>
using namespace std;
class Test {
public:
	Test(int t = 10)
		:_t(t)
	{
		_p = (int*)malloc(sizeof(int) * 5);
		cout << "Test: " << this << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test: " << this << endl;
	}

private:
	int _t;
	int* _p;
};

void Test1(Test** ptr) {
	delete *ptr;//反汇编je指令-》跳转，此处为跳转到析构函数
}

void MyTest() {
	Test* ptr = new Test;
	Test1(&ptr);
}
int main() {
	MyTest();
	return 0;
}
#endif


//关于delete在什么场景下调用析构的测试及delete的封装尝试
#ifdef a
#include<iostream>
using namespace std;
class Test {
public:
	Test(int t = 10)
		:_t(t)
	{
		_p = (int*)malloc(sizeof(int) * 5);
		cout << "Test: " << this << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test: " << this << endl;
	}

private:
	int _t;
	int* _p;
};

void* operator new(size_t size, const char* file_name, const char* function_name, int line_num) {
	cout << file_name << '-' << function_name << '-' << line_num << ':' << size << endl;
	return operator new(size);
}

//-----------------------------------------------------------------------
//delete 封装
void operator delete(void* p, const char* file_name, const char* function_name, int line_num) {
	cout << file_name << '-' << function_name << '-' << line_num << endl;
	
}
/*
void Delete (void** p) {
	operator delete(p, __FILE__, __FUNCDNAME__, __LINE__);
	delete p;
}
//错误	C2664	“void Delete(void**)”: 无法将参数 1 从“Test * *”转换为“void * *”	
*/

/*
void Delete(void* p) {
	operator delete(p, __FILE__, __FUNCDNAME__, __LINE__);
	delete p;-->不会调用析构
}
*/
void Delete(void* p) {
	operator delete(p, __FILE__, __FUNCDNAME__, __LINE__);
	delete (Test*)p;//会调用析构，因此js汇编命令出现在p为对应类型的额前提下。否则不会调用析构函数，只是调用operator delete
}
//----------------------------------------------------------------------------------
#define new new(__FILE__, __FUNCDNAME__, __LINE__)



void MyTest() {
	Test* ptr = new Test;
	//delete ptr 会调用;
	Delete(ptr);
}
int main() {
	MyTest();
	return 0;
}
#endif 


#ifdef a
//delete 封装
#include<iostream>
using namespace std;
class Test {
public:
	Test(int t = 10)
		:_t(t)
	{
		_p = (int*)malloc(sizeof(int) * 5);
		cout << "Test: " << this << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test: " << this << endl;
	}

private:
	int _t;
	int* _p;
};

void* operator new(size_t size, const char* file_name, const char* function_name, int line_num) {
	cout << file_name << '-' << function_name << '-' << line_num << ':' << size << endl;
	return operator new(size);
}

//-----------------------------------------------------------------------
//delete 封装
void operator delete(void* p, const char* file_name, const char* function_name, int line_num) {
	cout << file_name << '-' << function_name << '-' << line_num << endl;
	return operator delete(p);//调用operator delete
}

void Delete(void* p) {
	((Test*)p) -> ~Test();//先调用析构
	//用户可以自己调用析构函数，但不能主动调用构造函数（无法选择出生，但可以选择死亡）
	operator delete(p, __FILE__, __FUNCDNAME__, __LINE__);
}
//----------------------------------------------------------------------------------
#define new new(__FILE__, __FUNCDNAME__, __LINE__)

void MyTest() {
	Test* ptr = new Test;
	Delete(ptr);
}
int main() {
	MyTest();
	return 0;
}
#endif 

//如上多次测试可发现析构函数调用过于繁琐，且需要具体的类型，因此基本不使用.




#ifdef a
//进一步优化--》条件编译
#include<iostream>
using namespace std;
class Test {
public:
	Test(int t = 10)
		:_t(t)
	{
		_p = (int*)malloc(sizeof(int) * 5);
		cout << "Test: " << this << endl;
	}
	~Test() {
		free(_p);
		_p = nullptr;
		cout << "~Test: " << this << endl;
	}

private:
	int _t;
	int* _p;
};

void* operator new(size_t size, const char* file_name, const char* function_name, int line_num) {
	cout << file_name << '-' << function_name << '-' << line_num << ':' << size << endl;
	return operator new(size);
}

void operator delete(void* p, const char* file_name, const char* function_name, int line_num) {
	cout << file_name << '-' << function_name << '-' << line_num << endl;
	return operator delete(p);
}

void Delete(void* p) {
	((Test*)p) -> ~Test();
	operator delete(p, __FILE__, __FUNCDNAME__, __LINE__);
}

//--------------------------------------------------------
//条件编译（使宏在debug模式下使用，Realease模式下跳过），Dubug模式下方便调试，宏定义使得代码更方便
//但Realease模式会自动优化大量内容，强调实用性，不可调试
#ifdef _DEBUG
#define new new(__FILE__, __FUNCDNAME__, __LINE__)//Realease模式下new不是宏，只是普通new调用，会少第一行打印
#endif
//--------------------------------------------------------------
void MyTest() {
	Test* ptr = new Test;
	Delete(ptr);
}
int main() {
	MyTest();
	return 0;
}
#endif 

#ifdef a

//由于每次申请空间都会多申请32个byte用来管理和保护空间（32位下），因为多次连续申请
//就会浪费很多空间，有什么解决办法吗？

//内存池：
/*
class MemoryPool {
	void* Allocate(size_t size);
	void Realloc(void* ptr);
	char* start;
	char* end;
 };
 */
//直接通过malloc申请非常大的内存,以后每次申请空间都向Allocate索要，通过start，end赋予。

//但Allocate只能申请空间， 返回内存块，对于对象来说，无法完成初始化，要想实例化对象，必须对该空间执行构造函数。
#include<iostream>

using namespace std;
class Test {
public:
	Test(int t)
		:_t(t)
	{
		cout << "Test" << endl;
	}
	~Test() {
		cout << "~Test" << endl;
	}
private:
	int _t;

};
void MyTest() {
	//new (palce_address) type(initializer_list),两个参数：空间地址，初始化参数
	Test* ptr1 = (Test*)malloc(sizeof(Test));//在堆上申请了一块空间
	//要想创建对象，需要对该空间初始化为对象
	Test* p1 = new(ptr1)Test(10);//-->定位new/位置new，在已分配的原始内存空间中调用构造函数初始化一个对象
	//定位new不能使用delete释放内存，delete与new配套使用，而不是定位new
	cout << _CrtDumpMemoryLeaks() << endl;
	delete p1;
	cout << _CrtDumpMemoryLeaks() << endl;
	//new作为运算符，看到new就会执行两步操作，第一步就是调用void* operator new，只不过在定位new中
	//，参数为（size_t size, void* where)调用该函数后，内部不进行空间申请，直接return where;
	//然后直接进入第二步调用构造函数吧

	//对于定位new而言，其内存由常规new给出，定位new本身不开辟新内存，因此无法
	//直接用delete ptr1,直接使用并不会回收p1空间，因为p1空间本身由常规new申请，
	//需要使用delete[] buffer才能释放空间，但这样又会释放整个buffer空间，且不会调用
	//对象p1的析构函数，因此正确做法是显性调用pc1的析构函数对pc1进行释放。
	//注意析构顺序要与构造顺序相反。

	//定位new没有申请空间，所以不能使用delete释放。要使用delete释放，只能释放ptr1，但通过delete
	//[]/delete释放ptr1,又不会调用构造函数（内置类型），因此需要显性调用析构函数
}
int main() {
	MyTest();
	return 0;
}
#endif


#ifdef a
//如何申请4g空间（32bit）->64bit下可以
#include<iostream>
using namespace std;
int main() {
	void* test = new char[0xffffffff];
	cout << "new:" << test << endl;
	return 0;
}
#endif 
